/**类
介绍
传统的JavaScript程序使用函数和基于原型的继承来创建可重用的组件，但对于熟悉使用面向对象方式的程序员来讲就有些棘手，因为他们用的是基于类的继承并且对象是由类构建出来的。 从ECMAScript 2015，也就是ECMAScript 6开始，JavaScript程序员将能够使用基于类的面向对象的方式。 使用TypeScript，我们允许开发者现在就使用这些特性，并且编译后的JavaScript可以在所有主流浏览器和平台上运行，而不需要等到下个JavaScript版本。 */

//不能定义在其他文件夹中存在的类名
class Greeter{
  greeting: string;
  constructor(message:string){
    this.greeting = message;
  }
  greet(){
    return "hello,"+this.greeting;
  }
}
//类可以new出来，接口不行
let greeter4  = new Greeter("world");


class Animal{
  move(distanceInMeters: number = 0){
    console.log("animal moved ${distanceInMeters}m.");
  }
}
class MyDog extends Animal{
  bark(){
    console.log("mydog bark");
  }
}

const dog = new MyDog();
dog.bark();
dog.move(10);
dog.bark();



class AnimalPlus{
  name: string;
  constructor(theName: string) { this.name = theName; }
  move(distaceInMeters:number=0){
    console.log(`$[this.name} moved ${distaceInMeters}m.`);
  }
}
class Snake extends AnimalPlus{
  //派生类包含了一个构造函数，它 必须调用 super()，它会执行基类的构造函数
  constructor(name:string){super(name);}
  //入参可以给初始值
  move(distanceInMeters = 5){
    console.log("Slithering ... ");
    super.move(distanceInMeters)
  }
}
class Horse extends AnimalPlus{
  constructor(name:string){super(name);}
  move(distanceInMeters=56){
    console.log("Galloping ...");
    super.move(distanceInMeters);
  }
}

let sam = new Snake("Sammy the python");
let tom: AnimalPlus = new Horse("Tommy the Palomino");
sam.move(10);
tom.move();




class AnimalEx{
  public name:string;
  public construct(theName:string){
    this.name = theName;
  }
  public move(distanceInMeters : number){
    console.log(`${this.name} moved ${distanceInMeters}.`);
  }
}

class Animal3{
  private name:string;
  constructor(theName:string){
    this.name = theName;
  }
  getName(){
    return this.name;
  }
}

//new Animal3("mil").name;//error name是私有的，
new Animal3("keke").getName(); //通过方法获取name


class Animal4 {
  private name: string;
  constructor(theName: string) { this.name = theName; }
}

class Rhino extends Animal4 {
  constructor() { super("Rhino"); }
}

class Emp {
  private name: string;
  constructor(theName: string) { this.name = theName; }
}

let animal = new Animal4("Goat");
let rhino = new Rhino();
let employee = new Emp("Bob");

animal = rhino;
//animal = employee; // 错误: Animal4 与 Emp 不兼容.


/**构造函数也可以被标记成 protected。 这意味着这个类不能在包含它的类外被实例化，但是能被继承。比如，*/ 
class Person{
  protected name: string;
  protected constructor(theName:string){this.name = theName;}
}
class Employee extends Person{
  private department: string;
  constructor(name:string,department:string){
    super(name);
    this.department = department;
  }
  public getElevatorPitch(){
    return `hello,my name is ${this.name} and I work in ${this.department}`;
  }
}
let howard = new Employee("Howard","Sales");
//let join = new Person("Join")// 错误: 'Person' 的构造函数是被保护的. 



/**readonly修饰符
你可以使用 readonly关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。 */
class Octopus{
  readonly name: string;
  readonly numberOfLegs: number = 8;
  constructor(theName: string){
    this.name  = theName;
  }
}
let dad = new Octopus("Man with the 8 strong legs");
//dad.name = "Man with the 3-piece suit";//错误name 是只读的


class Employee2{
  fullName: string;
}
let employee2 = new Employee2();
employee2.fullName = "Bob Smith";
if(employee2.fullName){
  console.log(employee2.fullName);
}


let passcode = "secret passcode";

class EmployeePlus {
  private _fullName: string;

  get fullName(): string {
      return this._fullName;
  }

  set fullName(newName: string) {
      if (passcode && passcode == "secret passcode") {
          this._fullName = newName;
      }
      else {
          console.log("Error: Unauthorized update of employee!");
      }
  }
}


/**静态属性
在这个例子里，我们使用 static定义 origin，因为它是所有网格都会用到的属性。 每个实例想要访问这个属性的时候，都要在 origin前面加上类名。如同在实例属性上使用 this.前缀来访问属性一样
*/
class Grid{
  static origin = {x: 0 ,y : 0};
  calculateDistanceFormOrigin(point:{x:number;y:number}){
    let xDist = (point.x - Grid.origin.x);
    let yDist = (point.y - Grid.origin.y);
    return Math.sqrt(xDist * xDist + yDist * yDist)/ this.scale;
  }
  constructor(public scale:number){
  }
}
let gird1 = new Grid(1);
let gird2 = new Grid(2.0);
console.log(gird1.calculateDistanceFormOrigin({x:10,y:20}));

let point = {x:20,y:30}
console.log(gird2.calculateDistanceFormOrigin(point));


/**抽象类
抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口，抽象类可以包含成员的实现细节。 abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。
*/
abstract class Dept{
  constructor(public name: string){
  }
  printName():void{
    console.log('Dept name:'+this.name)
  }
}
class AccountingDept extends Dept{
  constructor(){
    super('Accounting and Auditing');//派生类的构造函数中心必须调用super()
  }
  printMeeting():void{
    console.log("The Accounting Dept meets each Monday ad 10am.");
  }
  generateReports():void{
    console.log("generatig accounting reports ...");
  }
}
let dept : Dept;
//dept = new Dept(); //错误: 不能创建一个抽象类的实例
dept = new AccountingDept();
dept.printName();
//dept.printMeeting();// 错误: 方法在声明的抽象类中不存在 ,重新new了都不行
//dept.generateReports();// 错误: 方法在声明的抽象类中不存在

let dept2 = new AccountingDept();
dept2.printName();
dept2.printMeeting();
dept2.generateReports();

